package compound.repository

import compound.repository.entries.Compound
import java.awt.Color
import java.awt.Graphics2D
import java.awt.Image
import java.awt.Rectangle
import java.awt.image.BufferedImage
import org.openscience.cdk.AtomContainer
import org.openscience.cdk.DefaultChemObjectBuilder
import org.openscience.cdk.Molecule
import org.openscience.cdk.aromaticity.CDKHueckelAromaticityDetector
import org.openscience.cdk.graph.ConnectivityChecker
import org.openscience.cdk.inchi.InChIToStructure
import org.openscience.cdk.interfaces.IAtomContainer
import org.openscience.cdk.io.SDFWriter
import org.openscience.cdk.layout.StructureDiagramGenerator
import org.openscience.cdk.renderer.Renderer
import org.openscience.cdk.renderer.font.AWTFontManager
import org.openscience.cdk.renderer.generators.BasicAtomGenerator
import org.openscience.cdk.renderer.generators.IGenerator
import org.openscience.cdk.renderer.generators.RingGenerator
import org.openscience.cdk.renderer.visitor.AWTDrawVisitor
import org.openscience.cdk.tools.CDKHydrogenAdder
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator

/**
 * this service is used to render a structure into a mol file or a graph
 */


class RenderService {

    boolean transactional = true

    /**
     * renders a mol file from a compound
     */
    String renderToMol(Compound compound) {
        assert compound != null, "sorry you need to provide a compound!"

        InChIToStructure convert = new InChIToStructure(compound.inchi, DefaultChemObjectBuilder.getInstance())
        AtomContainer container = (AtomContainer) convert.getAtomContainer()

        log.info container.class.getName()
        container.setProperty "InChI", compound.inchi
        container.setProperty "InChI Key", compound.inchiHashKey.completeKey
        container.setProperty "Exact Molare Mass", compound.exactMolareMass
        container.setProperty "Sum Formula", compound.formula



        StringWriter swriter = new StringWriter()
        SDFWriter writer = new SDFWriter(swriter)
        writer.write(container)
        writer.close()
        swriter.flush()
        swriter.close()

        String result = "${compound.id}\n${swriter.getBuffer().toString().trim()}".trim()

        return result

    }

    /**
     * generates an atom container from the inchi code
     */
    def renderToAtomContainer(String inchi) {

        assert inchi != null, "sorry you need to provide an inchi!"
        try {
            InChIToStructure convert = new InChIToStructure(inchi, DefaultChemObjectBuilder.getInstance())
            IAtomContainer container = convert.getAtomContainer()
            return container
        }
        catch (Exception e) {
            log.warn "${e.getMessage()} - ${inchi}"
            return new AtomContainer()
        }
    }

    /**
     * renders an inchi code to mol
     */
    String renderToMol(String inchi) {

        IAtomContainer container = renderToAtomContainer(inchi)
        StringWriter swriter = new StringWriter()
        SDFWriter writer = new SDFWriter(swriter)
        writer.write(container)
        writer.close()
        swriter.flush()
        swriter.close()

        String result = "1\n ${swriter.getBuffer().toString()}"
        log.debug "generated sdf: ${result}"
        return result

    }
    /**
     * renders an image from a mol file
     */
    def renderToImage(Compound compound, int width = 120, int height = 120, Color color = Color.WHITE) {
        return renderToImage(compound.inchi, width, height, color)
    }

/**
 * renders an image from a mol file
 */
    Image renderToImage(String inchi, int width = 120, int height = 120, Color color = Color.WHITE) {


        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)


        try {

            StructureDiagramGenerator sdg = new StructureDiagramGenerator()
            IAtomContainer mol = renderToAtomContainer(inchi)

            AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(mol);

            CDKHueckelAromaticityDetector.detectAromaticity(mol);
            CDKHydrogenAdder.getInstance(DefaultChemObjectBuilder.getInstance()).addImplicitHydrogens mol

            // generators make the image elements
            List<IGenerator> generators = new ArrayList<IGenerator>()
            generators.add(new RingGenerator())

            generators.add(new BasicAtomGenerator())

            // the renderer needs to have a toolkit-specific font manager
            Renderer renderer = new Renderer(generators, new AWTFontManager())

            renderer.getRenderer2DModel().setFitToScreen true
            renderer.getRenderer2DModel().setShowAtomTypeNames false
            renderer.getRenderer2DModel().setShowImplicitHydrogens true
            renderer.getRenderer2DModel().setShowExplicitHydrogens true
            renderer.getRenderer2DModel().setShowTooltip true

            renderer.getRenderer2DModel().setUseAntiAliasing true
            renderer.getRenderer2DModel().setBoundsColor Color.darkGray
            renderer.getRenderer2DModel().setScale 0.8

            // the call to 'setup' only needs to be done on the first paint
            Rectangle drawArea = new Rectangle(width, height)

            if (ConnectivityChecker.isConnected(mol)) {
                sdg.setMolecule(new Molecule(mol))
                sdg.generateCoordinates()
                mol = sdg.getMolecule()



                renderer.setup(mol, drawArea);

                // paint the background
                Graphics2D g2 = (Graphics2D) image.getGraphics();
                g2.setColor(color);
                g2.fillRect(0, 0, width, height);

                // the paint method also needs a toolkit-specific renderer
                renderer.paintMolecule(mol, new AWTDrawVisitor(g2), drawArea, true);
            }
            else {

                noStructure(image)

            }
        }
        catch (Exception e) {
            log.warn "${e.getMessage()} - ${inchi}", e

            noStructure(image)

        }
        return image

    }

    private def noStructure(BufferedImage image) {
        Graphics2D graphics = (Graphics2D) image.getGraphics()
        graphics.setBackground Color.white
        graphics.setColor Color.black

        graphics.drawString "no strucutre available", 0, 0
    }

}
